package com.ds.lens.data.service;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.ds.lens.data.common.asserts.Asserts;
import com.ds.lens.data.common.client.vo.LensExternalEventInVO;
import com.ds.lens.data.common.constant.Constants;
import com.ds.lens.data.common.constant.EventSourceEnum;
import com.ds.lens.data.common.constant.EventStateEnum;
import com.ds.lens.data.common.document.Notify;
import com.ds.lens.data.common.document.dto.MonitorEvent;
import com.ds.lens.data.common.document.dto.MonitorKey;
import com.ds.lens.data.service.dao.LensExternalEventDao;
import com.ds.lens.data.service.dao.entity.LensExternalEvent;
import com.ds.lens.data.service.dao.entity.LensMonitor;
import com.ds.lens.data.vo.out.v2.event.external.ExternalEventTaskOutVO;
import com.ds.lens.data.vo.out.v2.monitor.LensMonitorDetailsOutVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * Description:
 *
 * @author WeiShaoying
 * @date 2020-04-10
 */
@Slf4j
@Service
public class LensExternalEventService extends BaseService {
    private static final Integer OVERTIME = 30;
    private static final Integer MAX_TIMES = 3;
    @Autowired
    private LensExternalEventDao externalEventDao;
    @Autowired
    private LensMonitorService lensMonitorService;

    public List<ExternalEventTaskOutVO> getExternalEventTasks() {
        List<LensExternalEvent> externalEvents = externalEventDao.selectEventTasks(MAX_TIMES);
        if (CollectionUtils.isEmpty(externalEvents)) {
            return Collections.emptyList();
        }

        List<ExternalEventTaskOutVO> tasks = Lists.newArrayListWithCapacity(externalEvents.size());

        externalEvents.forEach(lensExternalEvent -> {
            ExternalEventTaskOutVO outVO = new ExternalEventTaskOutVO();
            outVO.setExternalEventId(lensExternalEvent.getId());
            int reminders = lensExternalEvent.getReminders();
            outVO.setReminders(reminders);
            if (reminders > 0 && lensExternalEvent.getLastReminderTime() != null) {
                outVO.setExecutionTime(DateUtils.addMinutes(lensExternalEvent.getLastReminderTime(), OVERTIME).getTime());
            } else {
                outVO.setExecutionTime(DateUtils.addMinutes(lensExternalEvent.getStartTime(), OVERTIME).getTime());
            }
            tasks.add(outVO);
        });

        return tasks;
    }

    public void sendTimeoutNotice(Long externalEventId) {
        Asserts.assertNotNull(externalEventId, Constants.ErrorCode.PARAM_ERROR, "externalEventId can not be null.");
        LensExternalEvent lensExternalEvent = externalEventDao.selectByPrimaryKey(externalEventId);
        if (null == lensExternalEvent || lensExternalEvent.getStatus() != Constants.ExternalEventStatusEnum.STARTED.getCode()
                || lensExternalEvent.getReminders() > MAX_TIMES) {
            return;
        }
        LensMonitorDetailsOutVO monitorDetail = lensMonitorService.getMonitorDetails(lensExternalEvent.getMonitorId());
        if (null == monitorDetail) {
            return;
        }

        MonitorEvent monitorEvent = MonitorEvent.builder()
                .uid(UUID.randomUUID().toString())
                .monitorName(monitorDetail.getName())
                .monitorKey(new MonitorKey(monitorDetail.getId(), monitorDetail.getVersion()))
                .tags(ArrayUtils.isNotEmpty(monitorDetail.getTags()) ? Lists.newArrayList(monitorDetail.getTags()) : null)
                .appId(monitorDetail.getApplicationId())
                .state(EventStateEnum.INFO)
                .timeStamp(System.currentTimeMillis())
                .notify(Notify.builder()
                        .content("运维事件[" + lensExternalEvent.getExternalEventType() + "]已超时，请关注。")
                        .title(monitorDetail.getNotify().getTitle())
                        .channel(monitorDetail.getNotify().getChannel())
                        .channelContent(monitorDetail.getNotify().getChannelContent())
                        .build())
                .eventSourceEnum(EventSourceEnum.EXTERNAL_EVENT)
                .isNotify(true)
                .build();
        //Send message
        kafkaProducer.sendMessage(eventTopic, null, JSONObject.toJSONString(monitorEvent));
        //Update reminders and lastReminderTime
        externalEventDao.updateRemindersById(lensExternalEvent.getId(), lensExternalEvent.getReminders(), new Date());
    }

    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Throwable.class)
    public void startExternalEvent(LensExternalEventInVO lensExternalEventInVO) {
        Asserts.assertNotNull(lensExternalEventInVO, Constants.ErrorCode.PARAM_ERROR, "lensExternalEventInVO can not be null.");
        Asserts.assertNotNull(lensExternalEventInVO.getStartTime(), Constants.ErrorCode.PARAM_ERROR, "startTime can not be null.");

        //查询是否存在未闭合的事件
        LensExternalEvent externalEvent = externalEventDao.selectLastEvent(
                lensExternalEventInVO.getMonitorId(),
                lensExternalEventInVO.getExternalEventType());

        if (null != externalEvent) {
            //如果存在则闭合原事件 并开启新事件
            autoCompletedEvent(lensExternalEventInVO.getStartTime(), externalEvent);
        }

        LensExternalEvent lensExternalEvent = new LensExternalEvent();
        lensExternalEvent.setMonitorId(lensExternalEventInVO.getMonitorId());
        lensExternalEvent.setVersion(lensExternalEventInVO.getVersion());
        lensExternalEvent.setExternalEventType(lensExternalEventInVO.getExternalEventType());
        lensExternalEvent.setStartTime(new Date(lensExternalEventInVO.getStartTime()));
        lensExternalEvent.setCreatedTime(new Date());

        Asserts.assertTrue(externalEventDao.insertSelective(lensExternalEvent) > 0, Constants.ErrorCode.SYSTEM_EXCEPTION);
        //更新monitor运行状态
        LensMonitor lensMonitor = lensMonitorDao.selectByPrimaryKey(lensExternalEventInVO.getMonitorId());
        if (null == lensMonitor || lensMonitor.getIsDelete() || lensMonitor.getRunStatus() == Constants.MonitorRunStatusEnum.SUSPENDED.getCode()) {
            return;
        }
        LensMonitor update = new LensMonitor();
        update.setId(lensMonitor.getId());
        update.setModifiedTime(new Date());
        update.setRunStatus(Constants.MonitorRunStatusEnum.SUSPENDED.getCode());
        Asserts.assertTrue(lensMonitorDao.updateByPrimaryKeySelective(update) > 0, Constants.ErrorCode.SYSTEM_EXCEPTION);
    }

    private void autoCompletedEvent(Long time, LensExternalEvent externalEvent) {
        LensExternalEvent updateEvent = new LensExternalEvent();
        updateEvent.setId(externalEvent.getId());
        updateEvent.setEndTime(new Date(time));
        updateEvent.setStatus(Constants.ExternalEventStatusEnum.AUTO_COMPLETION.getCode());
        Asserts.assertTrue(externalEventDao.updateByPrimaryKeySelective(updateEvent) > 0, Constants.ErrorCode.SYSTEM_EXCEPTION);
    }

    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Throwable.class)
    public void completeExternalEvent(LensExternalEventInVO lensExternalEventInVO) {
        Asserts.assertNotNull(lensExternalEventInVO, Constants.ErrorCode.PARAM_ERROR, "lensExternalEventInVO can not be null.");
        Asserts.assertNotNull(lensExternalEventInVO.getEndTime(), Constants.ErrorCode.PARAM_ERROR, "endTime can not be null.");
        Date endTime = new Date(lensExternalEventInVO.getEndTime());

        //查询事件
        LensExternalEvent externalEvent = externalEventDao.selectExternalEvent(
                lensExternalEventInVO.getMonitorId(),
                lensExternalEventInVO.getExternalEventType(),
                endTime, true);
        if (null == externalEvent) {
            return;
        }

        LensExternalEvent updateEvent = new LensExternalEvent();
        updateEvent.setId(externalEvent.getId());
        updateEvent.setEndTime(endTime);
        updateEvent.setStatus(Constants.ExternalEventStatusEnum.COMPLETION.getCode());

        Asserts.assertTrue(externalEventDao.updateByPrimaryKeySelective(updateEvent) > 0, Constants.ErrorCode.SYSTEM_EXCEPTION);

        //更新monitor运行状态
        LensMonitor lensMonitor = lensMonitorDao.selectByPrimaryKey(lensExternalEventInVO.getMonitorId());
        if (null == lensMonitor || lensMonitor.getIsDelete() || lensMonitor.getRunStatus() == Constants.MonitorRunStatusEnum.NORMAL.getCode()) {
            return;
        }
        LensMonitor update = new LensMonitor();
        update.setId(lensMonitor.getId());
        update.setModifiedTime(new Date());
        update.setRunStatus(Constants.MonitorRunStatusEnum.NORMAL.getCode());
        Asserts.assertTrue(lensMonitorDao.updateByPrimaryKeySelective(update) > 0, Constants.ErrorCode.SYSTEM_EXCEPTION);

    }
}
